<?php

/**
 *	IFXField Class
 *
 *	@package IntuiFormX
 *	@author Jeremy Clifton <j.clifton@4-8-4.com>
 */
class IFXField
{

	/** 
	 *	CHECKBOX AND RADIO BUTTON ONLY: The value that the generated checkbox will pass if it is checked. 
	 *
	 *	@access private
	 *	@type string
	 */	
	private $checkedVal	= null;
	
	/** 
	 *	TEXTAREA ONLY: The number of columns the generated textarea will display.
	 *
	 *	@access private
	 *	@type int
	 */
	private $cols = 50;
	
	/**
	 *	The element's class.
	 *
	 *	@access private
	 *	@type string
	 */
	private $class = null;
	
	/** 
	 *	The current value of the generated form field. Can be an array for select/dropdowns.
	 *
	 *	@access private
	 *	@type string 
	 */
	private $currentVal = null;				
	
	/** 
	 *	The default value of the generated form field (if $currentVal isn't set). 
	 *
	 *	@access private
	 *	@type string (or array for SELECT)
	 */
	private $defaultVal = null;
	
	/**
	 *	Determines if the field is disabled or not.
	 *
	 *	@access private
	 *	@type boolean
	 */
	private $disabled = false;
	
	/**
	 *	Reference to the form object that this is being added to.
	 *
	 *	@access private
	 *	@type IntuiForm object
	 */
	private $fObj = null;
	
	/**
	 *	The field's DOM id.
	 *
	 *	@access private
	 *	@type string
	 */
	private $id = null;
	
	/** 
	 *	Label for the form field.
	 *
	 *	@access private
	 *	@type string 
	 */
	private $label = null;
	
	/**
	 *	Label class(es).
	 *
	 *	@access private
	 *	@type string
	 */
	private $labelClass = null;
	
	/** 
	 *	TEXT, PASSWORD ONLY: The length (in characters) of the generated textbox.
	 *
	 *	@access private
	 *	@type int 
	 */
	private $len = null;						
	
	/** 
	 *	TEXT, PASSWORD ONLY: The maximum number of characters the generated textbox will accept as input. 
	 *
	 *	@access private
	 *	@type int
	 */
	private $maxLen = null;					
	
	/** 
	 *	SELECT ONLY: Indicates that the generated select will display multiple lines of input.
	 *
	 *	@access private
	 *	@type boolean 
	 */
	private $multi = null;						
	
	/** 
	 *	SELECT ONLY: The number of lines of options that the generated select will display.
	 *
	 *	@access private
	 *	@type int 
	 */
	private $multiSize = null;					
	
	/** 
	 *	The id/name for the generated form field. 
	 *
	 *	@access private
	 *	@type string
	 */
	private $name = null;
	
	/**
	 *	SELECT ONLY. The array holding the options and option groups.
	 *
	 *	@access private
	 *	@type array
	 */
	private $optArray = array();
	
	/**
	 *	Stores the level of indentation for select options.
	 *
	 *	@access private
	 *	@type int
	 */
	private $optIndent = 0;
	
	/**
	 *	ALL: This field is read-only.
	 *
	 *	@access private
	 *	@type bool
	 */
	private $readonly = false;
	
	/**
	 *	ALL: This field is required.
	 *	
	 *	@access private
	 *	@type bool
	 */
	private $required = false;
	
	/** 
	 *	TEXTAREA ONLY: The number of rows the generated textarea will display.
	 *
	 *	@access private
	 *	@type int 
	 */
	private $rows = 3;
	
	/** 
	 *	The template to be used to render this field and its label if [form]->template() is called.
	 *
	 *	@access private
	 *	@type int
	 */
	private $template = null;
	
	/**
	 *	The type of field this object represents.
	 *
	 *	@access private
	 *	@type string
	 */
	private $type = null;
	
	/**
	 *	A regex string used to validate input for this form field.
	 *
	 *	@access private
	 *	@type string
	 */
	private $validator = null;
	
	/** 
	 *	SELECT ONLY: An array of name/value pairs that represent possible values for the generated select.
	 *
	 *	@access private
	 *	@type array 
	 */
	private $vals = array();					
	
	/**
	 *	IntuiFormField constructor. Creates a new form field.
	 *
	 *	@param array $params
	 *	@return object -- The IntuiFormField object.
	 */
	public function __construct($params = array())
	{
	
		$this->fObj	= $params['formobject'];
		$this->id = $params['name'];
		$this->name = $params['name'];
		$this->type = $params['type'];
		$this->setFieldParams($params);
		// for checkboxes and radio buttons, if no label was set, we'll use the value
		if ($type == 'checkbox' or $type == 'radio') {
			if (empty($this->label)) {
				$this->label = $this->checkedVal;
			}
		}
		
	}
	
	/**
	 *	Adds the specified class name to the CSS classes if it doesn't already exist.
	 *
	 *	@param string $class_name
	 */
	public function addClass($class_name) { if (!preg_match('/\b'.$class_name.'\b/', $this->class)) $this->class .= ' '.$class_name; }

	/**
	 *	Adds the specified class name to the CSS classes if it doesn't already exist.
	 *
	 *	@param string $class_name
	 */	
	public function addLabelClass($class_name) { if (!preg_match('/\b'.$class_name.'\b/', $this->labelClass)) $this->labelClass .= ' '.$class_name; }
	
	/**
	 *	Retrieves the CSS class assigned to this field.
	 *
	 *	@return string
	 */
	public function getClass() { return $this->class; }
	
	/**
	 *	Retrieves the field's DOM id.
	 *
	 *	@return string
	 */
	public function getID() { return $this->id; }
	
	/**
	 *	Retrieves the field label.
	 *
	 *	@return string
	 */
	public function getLabel()
	{
		if ($this->required) $this->addLabelClass('ifx-required');
		return '<label id="lbl-'.$this->id.'" class="'.$this->labelClass.'" for="'.$this->name.'">'.$this->label.'</label>';
	}
	
	/**
	 *	Retrieves the CSS class assigned to this field's label.
	 *
	 *	@return string
	 */
	public function getLabelClass() { return $this->labelClass; }
	
	/**
	 *	Retrieves the name of this form field.
	 *
	 *	@return string
	 */
	public function getName() { return $this->name; }
	
	/**
	 *	Retrieves the template name assigned to this field.
	 *
	 *	@return string
	 */
	public function getTemplate() { return $this->template; }
	
	
	/**
	 *	Retrieves the field type.
	 *
	 *	@return string
	 */
	public function getType() { return $this->type; }
	
	/**
	 *	Retrieves the validator regex pattern.
	 *
	 *	@return string
	 */
	public function getValidator() { return $this->validator; }
	
	/**
	 *	Indicates if this form field is set as being required.
	 *
	 *	@return boolean
	 */
	public function isRequired() { return $this->required; }
	
	/**
	 *	Generates HTML code for this form field.
	 *
	 *	@access public
	 *	@return string -- HTML Code.
	 */
	public function render()
	{
		
		// If the currentVal property isn't set, and a default value exists, use it.
		if ($this->currentVal == null AND $this->defaultVal != null) {
			$this->currentVal = $this->defaultVal;
		}
		
		// if this field is required, add ifx-required to the classes
		if ($this->required) $this->addClass('ifx-required');
		
		// is this field disabled or readonly?
		if ($this->disabled === true) $this->disabled_attr = 'disabled="disabled"';
		if ($this->readonly === true) $this->readonly_attr = 'readonly="readonly"';
		
		$method	 = '_'.$this->type;
		if (method_exists($this, $method)) {
			return $this->$method();
		}
		
	}
	
	/**
	 *	Sets the checked value (checkbox only)
	 *
	 *	@param string $val
	 */
	public function setCheckedVal($val) { $this->checkedVal = $val; }
	
	/**
	 *	Sets the element's class.
	 *
	 *	@param string $val
	 */
	public function setClass($val) { $this->class = $val; }
	
	/**
	 *	Sets a CSS property for this form field.
	 *
	 *	@access public
	 *	@param string $prop -- The property name.
	 *	@param string $val -- The property's value.
	 */
	public function setCSS($prop, $val) { $this->css[$prop] = $val; }
	
	/**
	 *	Sets the number of this columns (textarea only).
	 *
	 *	@access public
	 *	@param string $val
	 */
	public function setCols($val) { $this->cols = $val; }
	
	/**
	 *	Sets the current value of this field.
	 *
	 *	@access public
	 *	@param mixed $val
	 */
	public function setCurrentVal($val) 
	{ 
		if ($this->type != 'select' and is_array($val)) {
			trigger_error('Cannot assign multiple default values to '.$this->type.' ('.$this->name.') fields!', E_USER_ERROR);
			exit;
		}
		$this->currentVal = $val; 
	}
	
	/**
	 *	Sets the default value of this field.
	 *
	 *	@access public
	 *	@param mixed $val
	 */
	public function setDefaultVal($val) 
	{ 
		if ($this->type != 'select' and is_array($val)) {
			trigger_error('Cannot assign multiple default values to '.$this->type.' ('.$this->name.') fields!', E_USER_ERROR);
			exit;
		}
		$this->defaultVal = $val; 
	}
	
	/**
	 *	Sets the disabled status of this field.
	 *
	 *	@access public
	 *	@param mixed $val
	 */
	public function setDisabled($val) { $this->disabled = (bool)$val; }
	
	/**
	 *	Allows setting of field parameters in bulk.
	 *
	 *	@param array $params
	 */
	public function setFieldParams($params = array())
	{
		foreach ($params as $k => $v) { 
			$this->$k = $v;
		}
		if (!empty($params['options'])) {
			$this->optArray = $params['options'];
		}
	}
	
	/**
	 *	Sets an element event handler for this form field.
	 *
	 *	@access public
	 *	@param string $handler -- The event handler name.
	 *	@param string $val -- The event handler value.
	 */
	public function setHandler($handler, $val) { $this->handlers[$handler]	= $val; }
	
	/**
	 *	Indicates that input in this field is invalid by adding the ifx-error class to the field and label.
	 *
	 *	@access public
	 */
	public function setInvalid()
	{
		$this->addClass('ifx-error');
		$this->addLabelClass('ifx-error');
	}
	
	/**
	 *	Sets the label for this form field
	 *
	 *	@access public
	 *	@param string $val
	 */
	public function setLabel($val) { $this->label = $val; }
	
	/**
	 *	Sets the label class for this form field
	 *
	 *	@access public
	 *	@param string $val
	 */
	public function setLabelClass($val) { $this->labelClass = $val; }
	
	/**
	 *	Sets the field length (text only)
	 *
	 *	@access public
	 *	@param int $val
	 */
	public function setLen($val) { $this->len = $val; }
	
	/**
	 *	Sets the maximum field length (text only)
	 *
	 *	@access public
	 *	@param int $val
	 */
	public function setMaxLen($val) { $this->maxLen = $val; }
	
	/**
	 *	Is this a multi select?
	 *
	 *	@access public
	 *	@param mixed $val
	 */
	public function setMulti($val) { $this->multi = (bool)$val; }
	
	/**
	 *	Sets the number of lines for a multi select box.
	 *
	 *	@access public
	 *	@param int $val
	 */
	public function setMultiSize($val) { $this->multiSize = $val; }
	
	/**
	 *	Sets the name for this field.
	 *
	 *	@access public
	 *	@param string $val
	 */
	public function setName($val) { $this->name = $val; }
	
	/**
	 *	Sets the options and option groups for a select box. Setting the values
	 *	with this function will override and clear any values set via setVals().
	 *
	 *	@access public
	 *	@param array $optArray
	 */
	public function setOptions($optArray = array()) { $this->optArray = $optArray; }
	
	/**
	 *	Sets if this field is required or not.
	 *
	 *	@access public
	 *	@param bool $val
	 */
	public function setRequired($val) { $this->required = $val; }
	
	/**
	 *	Sets the number of rows (textarea only)
	 *
	 *	@access public
	 *	@param int $val
	 */
	public function setRows($val) { $this->rows = $val; }
	
	/**
	 *	Sets the template for this field.
	 *
	 *	@access public
	 *	@param string $val
	 */
	public function setTemplate($val) { $this->template = $val; }
	
	/**
	 *	Sets the type for this field.
	 *
	 *	@access public
	 *	@param string $val
	 */
	public function setType($val) { $this->type = $val; }
	
	/**
	 *	Sets the validator regex pattern.
	 *
	 *	@access public
	 *	@param string $val
	 */
	public function setValidator($val) { $this->validator = $val; }
	
	/**
	 *	Validates the specified value against the validator regex pattern. If no value is specified, tries to
	 *	validate against the $_REQUEST superglobal. If no validator was defined and the field is not required, 
	 * 	returns true. If no validator is defined and the field IS required, will simply check to make sure the
	 *	field is not empty.
	 *
	 *	@access public
	 *	@param mixed value
	 *	@return bool
	 */
	public function validate($value = null)
	{
		
		if (is_null($this->validator) and !$this->required) return true;
		if (is_null($this->validator) and $this->required) {
			return !empty($value);
		} else {
			return (bool)preg_match($this->validator, $value);
		}
	}
	
	/**
	 *	Generates a HTML button.
	 *
	 *	@access private
	 *	@return string
	 */
	private function _button()
	{
		return <<<FLD
<input type="button" id="{$this->id}" name="{$this->name}" value="{$this->label}" class="{$this->class}" {$this->disabled_attr} />		
FLD;
	}
	
	/**
	 *	Generates a HTML checkbox.
	 *
	 *	@access private
	 *	@return string
	 */
	private function _checkbox()
	{
		if (is_array($this->currentVal)) {
			if (in_array($this->checkedVal, $this->currentVal)) $checked = 'checked="checked"';
		} else {
			if ($this->checkedVal == $this->currentVal) $checked = 'checked="checked"';
		}
		return <<<FLD
<input type="checkbox" id="{$this->id}" name="{$this->name}" value="{$this->checkedVal}" class="{$this->class}"
	{$checked} {$this->disabled_attr} {$this->readonly_attr} />
FLD;
	}
	
	/**
	 *	Generates a HTML file input.
	 *
	 *	@access private
	 *	@return string
	 */
	private function _file()
	{
		return <<<FLD
<input type="file" id="{$this->id}" name="{$this->name}" size="{$this->len}" class="{$this->class}"
	{$this->disabled_attr} />
FLD;
	}
	
	/**
	 *	Generates a HTML hidden input.
	 *
	 *	@access private
	 *	@return string
	 */
	private function _hidden()
	{
		return <<<FLD
<input type="hidden" id="{$this->id}" name="{$this->name}" value="{$this->currentVal}" class="{$this->class}"
	{$this->disabled_attr} />		
FLD;
	}
	
	/**
	 *	Generates a HTML password textbox.
	 *
	 *	@access private
	 *	@return string
	 */
	private function _password()
	{
		return <<<FLD
<input type="password" id="{$this->id}" name="{$this->name}" size="{$this->len}" maxlength="{$this->maxLen}"
	class="{$this->class}" {$this->disabled_attr} {$this->readonly_attr} />
FLD;
	}
	
	/**
	 *	Generates a HTML radio button.
	 *
	 *	@access private
	 *	@return string 
	 */
	private function _radio()
	{
		if ($this->checkedVal == $this->currentVal) $checked = 'checked="checked"';
		return <<<FLD
<input type="radio" id="{$this->id}" name="{$this->name}" value="{$this->checkedVal}" class="{$this->class}"
	{$checked}	{$this->disabled_attr} {$this->readonly_attr} />
FLD;
	}
	
	/**
	 *	Generates a HTML select/dropdown.
	 *
	 *	@access private
	 *	@return string
	 */
	private function _select()
	{
		
		// If this is a multiple select, we need to change the id.
		if ($this->multi) { $this->name	 = $this->name.'[]'; }
	
		// Create the select
		$element .= '<select id="'.$this->id.'" name="'.$this->name.'"';
		if ($this->multi) {
			$element .= ' multiple="multiple"';
		}
		if ($this->multiSize > 1) {
			$element .= ' size="'.$this->multiSize.'"';
		}
		if ($this->class) {
			$element .= ' class="'.$this->class.'"';
		}
		if ($this->disabled) {
			$element .= ' disabled="disabled"';
		}
		if ($this->readonly) {
			$element .= ' readonly="readonly"';
		}
		$element .= ' >'."\n";
		$element .= $this->_selectOptions($this->optArray);
		$element .= "</select>\n";
		
		return $element;
	
	}
	
	/**
	 *	Allows us to recursively create option groups in a select.
	 *
	 *	@param array $options
	 *	@return string
	 */
	private function _selectOptions($options)
	{
	
		// first let's see if the options are hashes or not ...
		if (sizeof($options) > 0 and !is_array($options[0])) {
			$new_opts = array();
			foreach ($options as $opt) {
				$new_opts[] = array('value' => $opt, 'label' => $opt);
			}
			$options = $new_opts;
		}
	
		$this->optIndent++;
		foreach ($options as $id => $arr) {
			// Check for option groups
			if ($arr['type'] == 'optionGroup') {
				for ($i = 1; $i <= $this->optIndent; $i++) {
					$opts	.= "\t";
				}
				$opts		.= '<optgroup label="'.$arr['label'].'"';
				if ($arr['id']) {
					$opts	.= ' id="'.$arr['id'].'"';
				}
				if ($arr['disabled']) {
					$opts	.= ' disabled="disabled"';
				}
				$opts		.= ">\n";
				$opts		.= $this->_selectOptions($arr['content']);
				for ($i = 1; $i <= $this->optIndent; $i++) {
					$opts	.= "\t";
				}
				$opts		.= "</optgroup>\n";
			} else {
				for ($i = 1; $i <= $this->optIndent; $i++) {
					$opts	.= "\t";
				}
				$opts		.= '<option value="'.$arr['value'].'"';
				if ( (is_array($this->currentVal) and in_array($arr['value'], $this->currentVal)) or $this->currentVal == $arr['value'] ) {
					$opts	.= ' selected="selected"';
				}
				if ($arr['id']) {
					$opts	.= ' id="'.$arr['id'].'"';
				}
				if ($arr['disabled']) {
					$opts	.= ' disabled="disabled"';
				}
				$opts		.= ">".$arr['label']."</option>\n";
			}
		}
		$this->optIndent--;
		return $opts;
	
	}
	
	/**
	 *	Generates a HTML submit button.
	 *
	 *	@access private
	 *	@return string -- HTML code.
	 */
	private function _submit()
	{
		return <<<FLD
<input type="submit" id="{$this->id}" name="{$this->name}" value="{$this->label}" class="{$this->class}" {$this->disabled_attr} />		
FLD;
	}
	
	/**
	 *	Generates a HTML textbox.
	 *
	 *	@access private
	 *	@return string
	 */
	private function _text()
	{
		return <<<FLD
<input type="text" id="{$this->id}" name="{$this->name}" size="{$this->len}" maxlength="{$this->maxLen}"
	value="{$this->currentVal}" class="{$this->class}" {$this->disabled_attr} {$this->readonly_attr} />	
FLD;
	}
	
	/**
	 *	Generates a HTML textarea input.
	 *
	 *	@access private
	 *	@return string
	 */
	private function _textarea()
	{
		return <<<FLD
<textarea id="{$this->id}" name="{$this->name}" rows="{$this->rows}" cols="{$this->cols}" class="{$this->class}"
	{$this->disabled_attr} {$this->readonly_attr}>{$this->currentVal}</textarea>		
FLD;
	}
	
}

?>